home *** CD-ROM | disk | FTP | other *** search
/ Apple WWDC 1996 / WWDC96_1996 (CD).toast / Technology Materials / QuickTime VR / MacOS / QuickDraw™ 3D 1.0.6F4 SDK / Samples / SampleCode / CustomAttribute / CustomAttributesSupport.c < prev    next >
Encoding:
Text File  |  1995-11-15  |  28.1 KB  |  960 lines  |  [TEXT/CWIE]

  1. // QuickDraw 3d Sample Code
  2. //
  3. // This file contains utility routines for QuickDraw 3d sample code.
  4. // Shows how to read a metafile and render it.
  5. //
  6. // Created 27th Dec 1994, Nick Thompson, DEVSUPPORT
  7.  
  8.  
  9. #include <Files.h>
  10. #include <QuickDraw.h>
  11. #include <QDOffScreen.h>
  12. #include <StandardFile.h>
  13. #include <TextUtils.h>
  14. #include <Strings.h>
  15.  
  16. #include "CustomAttributesSupport.h"
  17. #include "CustomAttributesShell.h"
  18.  
  19. #include <QD3D.h>
  20. #include <QD3DDrawContext.h>
  21. #include <QD3DRenderer.h>
  22. #include <QD3DShader.h>
  23. #include <QD3DCamera.h>
  24. #include <QD3DLight.h>
  25. #include <QD3DGeometry.h>
  26. #include <QD3DTransform.h>
  27. #include <QD3DGroup.h>
  28. #include <QD3DMath.h>
  29.  
  30. #include <QD3DStorage.h>
  31. #include <QD3DIO.h>
  32. #include <QD3DString.h>
  33.  
  34. #include "CustomAttribute_Lib.h"
  35.  
  36. //-----------------------------------------------------------------------------------------------
  37. // local utility functions
  38. static    TQ3FileObject         MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText ) ;
  39.  
  40. void GetGroupBBox(
  41.     DocumentPtr            theDocument,
  42.     TQ3BoundingBox         *viewBBox) ;
  43.                                                 
  44. static    TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) ;
  45.  
  46. static TQ3Status GetDocumentGroupBoundingBox( 
  47.     DocumentPtr theDocument , 
  48.     TQ3BoundingBox *viewBBox) ;
  49.  
  50. //-----------------------------------------------------------------------------------------------
  51. // Submit the scene for rendering/fileIO and picking
  52. TQ3Status SubmitScene( DocumentPtr theDocument ) 
  53. {        
  54.     Q3Style_Submit(theDocument->fInterpolation, theDocument->fView);
  55.     Q3Style_Submit(theDocument->fBackFacing , theDocument->fView);
  56.     Q3Style_Submit(theDocument->fFillStyle, theDocument->fView);
  57.         
  58.     Q3MatrixTransform_Submit( &theDocument->fRotation, theDocument->fView);
  59.         
  60.     Q3DisplayGroup_Submit( theDocument->fModel, theDocument->fView);
  61.     
  62.     return kQ3Success ;
  63. }
  64.  
  65. //-----------------------------------------------------------------------------------------------
  66.  
  67. static TQ3Status GetDocumentGroupBoundingBox( 
  68.     DocumentPtr theDocument , 
  69.     TQ3BoundingBox *viewBBox)
  70. {
  71.     TQ3Status        status;
  72.     TQ3ViewStatus    viewStatus ;
  73.     
  74.     status = Q3View_StartBoundingBox( theDocument->fView, kQ3ComputeBoundsApproximate );
  75.     do {
  76.         status = SubmitScene( theDocument ) ;
  77.     } while((viewStatus = Q3View_EndBoundingBox( theDocument->fView, viewBBox )) == kQ3ViewStatusRetraverse );
  78.     return status ;
  79. }
  80.  
  81. //-----------------------------------------------------------------------------------------------
  82.  
  83. TQ3ViewObject MyNewView(WindowPtr theWindow)
  84. {
  85.     TQ3Status                myStatus;
  86.     TQ3ViewObject            myView;
  87.     TQ3DrawContextObject        myDrawContext;
  88.     TQ3RendererObject        myRenderer;
  89.     TQ3CameraObject            myCamera;
  90.     TQ3GroupObject            myLights;
  91.     
  92.     myView = Q3View_New();
  93.     
  94.     //    Create and set draw context.
  95.     if ((myDrawContext = MyNewDrawContext(theWindow)) == nil )
  96.         goto bail;
  97.         
  98.     if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure )
  99.         goto bail;
  100.  
  101.     Q3Object_Dispose( myDrawContext ) ;
  102.     
  103.     // Create and set renderer.
  104.     //
  105.     // hacky way to do this, but since I wanted these snippets to have 
  106.     // a minimal interface, this will suffice
  107.     //
  108.     // change the next line to “#if 1” to use the WF renderer
  109.     
  110. #if 0
  111.     // this would use the wireframe renderer
  112.     myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame);
  113.     if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
  114.         goto bail;
  115.     }
  116. #else
  117.     // this would use the interactive software renderer
  118.  
  119.     if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != nil ) {
  120.         if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
  121.             goto bail;
  122.         }
  123.     }
  124.     else {
  125.         goto bail;
  126.     }
  127. #endif
  128.  
  129.     Q3Object_Dispose( myRenderer ) ;
  130.     
  131.     //    Create and set camera.
  132.     if ( (myCamera = MyNewCamera(theWindow)) == nil )
  133.         goto bail;
  134.         
  135.     if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure )
  136.         goto bail;
  137.  
  138.     Q3Object_Dispose( myCamera ) ;
  139.     
  140.     //    Create and set lights.
  141.     if ((myLights = MyNewLights()) == nil )
  142.         goto bail;
  143.         
  144.     if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure )
  145.         goto bail;
  146.         
  147.     Q3Object_Dispose(myLights);
  148.  
  149.     //    Done!!!
  150.     return ( myView );
  151.     
  152. bail:
  153.     //    If any of the above failed, then don't return a view.
  154.     return ( nil );
  155. }
  156.  
  157. //----------------------------------------------------------------------------------
  158.  
  159. TQ3DrawContextObject MyNewDrawContext(WindowPtr theWindow)
  160. {
  161.     TQ3DrawContextData        myDrawContextData;
  162.     TQ3MacDrawContextData    myMacDrawContextData;
  163.     TQ3ColorARGB            ClearColor;
  164.     TQ3DrawContextObject    myDrawContext ;
  165.     
  166.     ClearColor.a = 1.0;
  167.     ClearColor.r = 1.0;
  168.     ClearColor.g = 1.0;
  169.     ClearColor.b = 1.0;
  170.     
  171.     //    Fill in draw context data.
  172.     myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor;
  173.     myDrawContextData.clearImageColor = ClearColor;
  174.     
  175.     myDrawContextData.paneState = kQ3False;
  176.     myDrawContextData.maskState = kQ3False;
  177.     
  178.     myDrawContextData.doubleBufferState = kQ3True;
  179.  
  180.     myMacDrawContextData.drawContextData = myDrawContextData;
  181.     
  182.     myMacDrawContextData.window = (CGrafPtr) theWindow;        // this is the window associated with the view
  183.     myMacDrawContextData.library = kQ3Mac2DLibraryNone;
  184.     myMacDrawContextData.viewPort = nil;
  185.     myMacDrawContextData.grafPort = nil;
  186.     
  187.     //    Create draw context and return it, if it’s nil the caller must handle
  188.     myDrawContext = Q3MacDrawContext_New(&myMacDrawContextData) ;
  189.  
  190.     return myDrawContext ;
  191. }
  192.  
  193. //----------------------------------------------------------------------------------
  194.  
  195. TQ3CameraObject MyNewCamera(WindowPtr theWindow)
  196. {
  197.     TQ3CameraObject                    myCamera;
  198.     TQ3CameraData                    myCameraData;
  199.     TQ3ViewAngleAspectCameraData        myViewAngleCameraData;
  200.     TQ3Point3D                        cameraFrom     = { 0.0, 0.0, 1.5 };
  201.     TQ3Point3D                        cameraTo     = { 0.0, 0.0, 0.0 };
  202.     TQ3Vector3D                        cameraUp     = { 0.0, 1.0, 0.0 };
  203.     
  204.     float                             hither         = 0.4;
  205.     float                             yon         = 3.6;
  206.     
  207.     /*
  208.          we are going to set this up so that we have a view volume that measures 2x2x2
  209.          and is centered on 0,0,0.  In this demo, the default units are in meters, so 
  210.          that's a 2 meter box.
  211.     */
  212.     
  213.     //    Fill in camera data.
  214.     myCameraData.placement.cameraLocation = cameraFrom;
  215.     myCameraData.placement.pointOfInterest = cameraTo;
  216.     myCameraData.placement.upVector = cameraUp;
  217.     
  218.     myCameraData.range.hither = hither;
  219.     myCameraData.range.yon = yon;
  220.     
  221.     myCameraData.viewPort.origin.x = -1.0;
  222.     myCameraData.viewPort.origin.y = 1.0;
  223.     myCameraData.viewPort.width = 2.0;
  224.     myCameraData.viewPort.height = 2.0;
  225.     
  226.     myViewAngleCameraData.cameraData = myCameraData;
  227.     
  228.     // the half with is 1 at a distance of 1.5
  229.     myViewAngleCameraData.fov = 2 * atan( 1 / 1.5) ;
  230.     
  231.     // set up the aspect ratio based on the window
  232.     myViewAngleCameraData.aspectRatioXToY =  
  233.             (float) (theWindow->portRect.right - theWindow->portRect.left) / 
  234.             (float) (theWindow->portRect.bottom - theWindow->portRect.top);
  235.  
  236.     myCamera = Q3ViewAngleAspectCamera_New(&myViewAngleCameraData);    
  237.     
  238.     //    Return the camera.
  239.     return ( myCamera );
  240. }
  241.  
  242.  
  243. //----------------------------------------------------------------------------------
  244.  
  245. TQ3GroupObject MyNewLights()
  246. {
  247.     TQ3GroupPosition            myGroupPosition;
  248.     TQ3GroupObject            myLightList;
  249.     TQ3LightData                myLightData;
  250.     TQ3PointLightData        myPointLightData;
  251.     TQ3DirectionalLightData    myDirectionalLightData;
  252.     TQ3LightObject            myAmbientLight, myPointLight, myFillLight;
  253.     TQ3Point3D                pointLocation = { -10.0, 0.0, 10.0 };
  254.     TQ3Vector3D                fillDirection = { 10.0, 0.0, 10.0 };
  255.     TQ3ColorRGB                WhiteLight = { 1.0, 1.0, 1.0 };
  256.     
  257.     //    Set up light data for ambient light.  This light data will be used for point and fill
  258.     //    light also.
  259.  
  260.     myLightData.isOn = kQ3True;
  261.     myLightData.color = WhiteLight;
  262.     
  263.     //    Create ambient light.
  264.     myLightData.brightness = .2;
  265.     myAmbientLight = Q3AmbientLight_New(&myLightData);
  266.     if ( myAmbientLight == nil )
  267.         goto bail;
  268.     
  269.     //    Create point light.
  270.     myLightData.brightness = 1.0;
  271.     myPointLightData.lightData = myLightData;
  272.     myPointLightData.castsShadows = kQ3False;
  273.     myPointLightData.attenuation = kQ3AttenuationTypeNone;
  274.     myPointLightData.location = pointLocation;
  275.     myPointLight = Q3PointLight_New(&myPointLightData);
  276.     if ( myPointLight == nil )
  277.         goto bail;
  278.  
  279.     //    Create fill light.
  280.     myLightData.brightness = .2;
  281.     myDirectionalLightData.lightData = myLightData;
  282.     myDirectionalLightData.castsShadows = kQ3False;
  283.     myDirectionalLightData.direction = fillDirection;
  284.     myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
  285.     if ( myFillLight == nil )
  286.         goto bail;
  287.  
  288.     //    Create light group and add each of the lights into the group.
  289.     myLightList = Q3LightGroup_New();
  290.     if ( myLightList == nil )
  291.         goto bail;
  292.     myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight);
  293.     if ( myGroupPosition == 0 )
  294.         goto bail;
  295.     myGroupPosition = Q3Group_AddObject(myLightList, myPointLight);
  296.     if ( myGroupPosition == 0 )
  297.         goto bail;
  298.     myGroupPosition = Q3Group_AddObject(myLightList, myFillLight);
  299.     if ( myGroupPosition == 0 )
  300.         goto bail;
  301.  
  302.     Q3Object_Dispose( myAmbientLight ) ;
  303.     Q3Object_Dispose( myPointLight ) ;
  304.     Q3Object_Dispose( myFillLight ) ;
  305.  
  306.     //    Done!
  307.     return ( myLightList );
  308.     
  309. bail:
  310.     //    If any of the above failed, then return nothing!
  311.     return ( nil );
  312. }
  313.  
  314. //----------------------------------------------------------------------------------
  315.  
  316. TQ3GroupObject MyNewModelFromFile(FSSpec *theFileSpec, DocumentPtr theDocument)
  317. {
  318.     TQ3GroupObject        mainGroup = NULL, modelGroup = NULL;
  319.     TQ3Boolean            isText = kQ3False ;
  320.     TQ3FileMode            myFileMode = 0;
  321.     TQ3FileObject        theFile;
  322.     
  323.     //    Create a ordered group for all the elements that make up the group.
  324.     if ((mainGroup = Q3OrderedDisplayGroup_New()) == NULL )
  325.         return NULL;
  326.         
  327.     MyAddShaderToGroup( mainGroup ) ;
  328.  
  329.     //    Create a display group for the things that we read from the file.
  330.     if ((modelGroup = Q3DisplayGroup_New()) == NULL )
  331.         return NULL;
  332.  
  333.     // Add the model group to the document's group
  334.     
  335.     Q3Group_AddObject(mainGroup, modelGroup);
  336.     
  337.     theFile = MyGetNewFile( theFileSpec, &isText ) ;
  338.     
  339.     if( isText == kQ3True )
  340.         myFileMode |= kQ3FileModeText;    // is it a text metafile??    
  341.  
  342.     // Open the file object
  343.     if( Q3File_OpenRead( theFile, &myFileMode ) != kQ3Success)
  344.         return  NULL ;
  345.  
  346.     theDocument->fModel = mainGroup;
  347.  
  348.     if( MyReadModelFromFile( theFile, modelGroup, theDocument ) == kQ3Failure)
  349.         DebugStr("\pMetafile data read is null") ;
  350.     
  351.     Q3File_Close(theFile);            // close and dispose of the file object
  352.     Q3Object_Dispose(theFile);
  353.     
  354.     
  355.     return mainGroup ;
  356. }
  357.  
  358.  
  359. //----------------------------------------------------------------------------------
  360. // attach a shader to the group
  361.  
  362. TQ3Status MyAddShaderToGroup( TQ3GroupObject group )
  363. {
  364.     TQ3ShaderObject    illuminationShader = Q3PhongIllumination_New();
  365.  
  366.     Q3Group_AddObject(group, illuminationShader);
  367.     Q3Object_Dispose(illuminationShader);
  368.     return(kQ3Success);
  369. }
  370.  
  371. //----------------------------------------------------------------------------------
  372. // read model from file object into the supplied group
  373.  
  374. TQ3Status MyReadModelFromFile( TQ3FileObject theFile,TQ3GroupObject myGroup, DocumentPtr theDocument)
  375. {
  376.     TQ3Boolean    alreadyGotAttributes = kQ3False;
  377.     
  378.     if(theFile != NULL) {
  379.     
  380.         TQ3Object            myTempObj ;
  381.         TQ3Boolean            isEOF ;
  382.                 
  383.     
  384.         // read objects from the file
  385.         do {
  386.         
  387.             myTempObj = Q3File_ReadObject( theFile );
  388.             
  389.             if( myTempObj != NULL ) {
  390.                 // we only want the object in our main group if we can draw it
  391.                 if ( Q3Object_IsDrawable( myTempObj) ) {
  392.                     
  393.                     if( alreadyGotAttributes == kQ3False) {
  394.                         TQ3SetObject        set = NULL, groupSet = NULL;
  395.                         
  396.                         /*
  397.                             It only makes sense to interpret attributes like scale, up vector, and front vector on a file
  398.                             as a whole.  So, once we find one, that's the one we are going to use.
  399.                         */
  400.                         if( Q3Object_IsType(myTempObj, kQ3ShapeTypeGeometry) == kQ3True) {
  401.                             Q3Geometry_GetAttributeSet(myTempObj, &set);
  402.                         }
  403.                         
  404.                         if(set == NULL && Q3Object_IsType(myTempObj, kQ3SharedTypeShape) == kQ3True){
  405.                             Q3Shape_GetSet(myTempObj, &set);
  406.                         }
  407.                         
  408.                         // we are going to migrate the attributes up
  409.                         Q3Shape_GetSet(theDocument->fModel, &groupSet);
  410.                         if( groupSet == NULL) {
  411.                             groupSet = Q3Set_New();
  412.                             if( groupSet ) {
  413.                                 Q3Shape_SetSet(theDocument->fModel, groupSet);
  414.                             }
  415.                         }            
  416.  
  417.                         if( set ) {
  418.                             if( Q3Set_Contains( set, kElementTypeScale) == kQ3True ) {
  419.                                 double    scale;
  420.                                 
  421.                                 Q3Set_Get(set, kElementTypeScale, &scale);
  422.                                 
  423.                                 alreadyGotAttributes = kQ3True;
  424.                                 
  425.                                 Q3Shape_GetSet(theDocument->fModel, &groupSet);
  426.                                 
  427.                                 if( groupSet )
  428.                                     Q3Set_Add(groupSet, kElementTypeScale, &scale);
  429.                             }
  430.                     
  431.                             if( Q3Set_Contains( set, kElementTypeUpVector) == kQ3True ) {
  432.                                 TQ3Vector3D    upVector;
  433.                                 
  434.                                 Q3Set_Get(set, kElementTypeUpVector, &upVector);
  435.                                 
  436.                                 alreadyGotAttributes = kQ3True;
  437.                                 
  438.                                 if( groupSet )
  439.                                     Q3Set_Add(groupSet, kElementTypeUpVector, &upVector);
  440.                             }
  441.                             
  442.                             if( Q3Set_Contains( set, kElementTypeForwardDirection) == kQ3True ) {
  443.                                 TQ3Vector3D    forwardVector;
  444.                                 
  445.                                 Q3Set_Get(set, kElementTypeForwardDirection, &forwardVector);
  446.                                 
  447.                                 alreadyGotAttributes = kQ3True;
  448.                                 
  449.                                 if( groupSet )
  450.                                     Q3Set_Add(groupSet, kElementTypeForwardDirection, &forwardVector);
  451.                             }
  452.                             
  453.                             Q3Object_Dispose(set);
  454.                             
  455.                             // balances both the get or the new
  456.                             if( groupSet )
  457.                                 Q3Object_Dispose(groupSet);
  458.                         }
  459.                     }            
  460.                     
  461.                     // Note that since myGroup is a display group, we preserve the order of the objects in the file
  462.                     Q3Group_AddObject( myGroup, myTempObj ) ;
  463.                 }
  464.                 
  465.                 // we either added the object to the main group, or we don't care
  466.                 // so we can safely dispose of the object
  467.                 Q3Object_Dispose( myTempObj ) ;
  468.             }
  469.             
  470.             // check to see if we reached the end of file yet
  471.             isEOF = Q3File_IsEndOfFile( theFile );
  472.             
  473.         } while (isEOF == kQ3False);    
  474.     }
  475.     
  476.     if( alreadyGotAttributes != kQ3True ) {
  477.         StopAlert( NoScale_Alert, nil ) ;
  478.         return kQ3Failure;
  479.     }
  480.         
  481.     if( myGroup != NULL )
  482.         return kQ3Success ;
  483.     else
  484.         return kQ3Failure ;
  485. }
  486.  
  487. //-----------------------------------------------------------------------------------------------
  488. // cleaned up from IM QuickDraw 3D pp 15-5
  489. static TQ3FileObject MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText )
  490. {
  491.     TQ3FileObject        myFileObj;
  492.     TQ3StorageObject        myStorageObj;
  493.     OSType                myFileType;
  494.     
  495.     FInfo                fndrInfo ;
  496.  
  497.     // we assume the FSSpec passed in was valid, get the file information
  498.     // we need to know the file type, this routine may get called by an appleEvent
  499.     // handler, so we can't assume a type, we need to get it from the fsspec.
  500.     
  501.     FSpGetFInfo( myFSSpec, &fndrInfo ) ;
  502.     
  503.     // pull out the file type
  504.     
  505.     myFileType = fndrInfo.fdType ;
  506.     
  507.     // Create new storage object and new file object 
  508.     if(((myStorageObj = Q3FSSpecStorage_New( myFSSpec )) == NULL) 
  509.         || ((myFileObj = Q3File_New()) == NULL)) 
  510.     {
  511.         if (myStorageObj != NULL) 
  512.             Q3Object_Dispose(myStorageObj);
  513.         return(NULL);
  514.     }
  515.  
  516.     // Set the storage for the file object
  517.     Q3File_SetStorage(myFileObj, myStorageObj);
  518.     Q3Object_Dispose(myStorageObj);
  519.  
  520.     if (myFileType == '3DMF')
  521.         *isText = kQ3False ;
  522.     else if (myFileType == 'TEXT')
  523.         *isText = kQ3True ;
  524.  
  525.     return (myFileObj);
  526. }
  527.  
  528.  
  529. //-------------------------------------------------------------------------------------------
  530. //
  531. Boolean MetafileFileSpecify( FSSpec *theFile )
  532. {
  533.     StandardFileReply    theSFReply ;
  534.     SFTypeList            myTypes = { '3DMF' } ;
  535.     const short            numTypes = 1 ;
  536.         
  537.     // Get the file name to open
  538.     StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
  539.     
  540.     if( theSFReply.sfGood )
  541.         *theFile = theSFReply.sfFile ;
  542.     
  543.     // did the user cancel?
  544.     return theSFReply.sfGood ;
  545.     
  546. }
  547. //----------------------------------------------------------------------------------
  548.  
  549.  
  550. void GetGroupBBox(
  551.     DocumentPtr            theDocument,
  552.     TQ3BoundingBox         *viewBBox)
  553. {
  554.     TQ3Point3D                     from     = { 0.0, 0.0, 1.0 };
  555.     TQ3Point3D                     to         = { 0.0, 0.0, 0.0 };
  556.     TQ3Vector3D                 up         = { 0.0, 1.0, 0.0 };
  557.     
  558.     float                         fieldOfView = .52359333333;
  559.     float                         hither         =  0.5;
  560.     float                         yon         =  1.5;
  561.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  562.  
  563.     TQ3Status                    status;
  564.     
  565. #ifdef BETA_1_BUILD
  566.     Q3View_StartBounds( theDocument->fView );
  567.  
  568.     status = Q3DisplayGroup_BoundingBox(mainGroup, 
  569.                                         viewBBox, 
  570.                                         kQ3ComputeBoundsApproximate,
  571.                                          viewObject);
  572.  
  573.     Q3View_EndBounds( theDocument->fView );
  574. #else
  575.     status = GetDocumentGroupBoundingBox( theDocument , viewBBox) ;
  576. #endif
  577.                                         
  578.     //
  579.     //  If we have a point model, then the "viewBBox" would end up
  580.     //  being a "singularity" at the location of the point.  As
  581.     //  this bounding "box" is used in setting up the camera spec,
  582.     //  we get bogus input into Escher.
  583.     
  584.     {
  585.          float        xSize, ySize, zSize;
  586.         
  587.         xSize = viewBBox->max.x - viewBBox->min.x;
  588.         ySize = viewBBox->max.y - viewBBox->min.y;
  589.         zSize = viewBBox->max.z - viewBBox->min.z;
  590.  
  591.         if (xSize <= kQ3RealZero &&
  592.             ySize <= kQ3RealZero &&
  593.             zSize <= kQ3RealZero) {
  594.             
  595.             viewBBox->max.x += 0.0001;
  596.             viewBBox->max.y += 0.0001;
  597.             viewBBox->max.z += 0.0001;
  598.             
  599.             viewBBox->min.x -= 0.0001;
  600.             viewBBox->min.y -= 0.0001;
  601.             viewBBox->min.z -= 0.0001;
  602.         }
  603.     }
  604. }
  605.  
  606.  
  607.  
  608.  
  609. //------------------------------------------------------------------------
  610.  
  611.  
  612. void AdjustCamera(
  613.     DocumentPtr            theDocument,
  614.     short                winWidth,
  615.     short                winHeight)
  616. {
  617.     TQ3BoundingBox                 viewBBox;
  618.     TQ3CameraObject                camera;
  619.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  620.     TQ3ViewObject                theView = theDocument->fView;
  621.     TQ3Vector3D                    objectFront;
  622.     
  623.     Q3View_GetCamera( theView, &camera);
  624.     GetGroupBBox( theDocument, &viewBBox);
  625.  
  626.     if( viewBBox.isEmpty == kQ3False ) {
  627.         float                                width,
  628.                                             length,
  629.                                             height,
  630.                                             largestDimension;
  631.                                             
  632.         TQ3TransformObject                    scaleTransform = NULL,
  633.                                             orientationTransform = NULL,
  634.                                             centerTransform = NULL;
  635.                                             
  636.         TQ3SetObject                        set;
  637.             
  638.         double                                scale;
  639.         TQ3Vector3D                            scaleTransformData={1.0, 1.0, 1.0};
  640.         TQ3RotateAboutAxisTransformData        rotateTransform={{0,0,0},{0,0,1},0};
  641.         TQ3Matrix4x4                        orientationMatrix = {    1.0, 0.0, 0.0, 0.0,
  642.                                                                     0.0, 1.0, 0.0, 0.0,
  643.                                                                     0.0, 0.0, 1.0, 0.0,
  644.                                                                     0.0, 0.0, 0.0, 1.0};
  645.         /*
  646.             The ordered group, theDocument->fModel, is going to contain the following transforms
  647.             
  648.             first a scale transform to take care of the scale attribute
  649.             second a matrix with the rotations to take care of the up and forward vectors info
  650.             third a translate transform to position the object starting at 0 in z and centered in x and y
  651.             
  652.             remember that the transforms are applied in the reverse order, which is to say that the
  653.             resulting point p' from point p is
  654.             
  655.             p' = scale * rotation * translate * p
  656.         */
  657.         
  658.         // initialize the scale and orientation transforms
  659.         
  660.         // add an identify transform for the scale, values will be set later if needed
  661.         scaleTransform = Q3ScaleTransform_New(&scaleTransformData);
  662.         if( scaleTransform )
  663.             Q3Group_AddObject(mainGroup, scaleTransform);
  664.  
  665.         // calculate the extents of the bounding box
  666.         width = viewBBox.max.x - viewBBox.min.x;
  667.         height = viewBBox.max.y - viewBBox.min.y;
  668.         length = viewBBox.max.z - viewBBox.min.z;
  669.  
  670.         // add an identify transform for the orientation, values will be set later if needed
  671.         
  672.         // the object's center will be set at 0,0,-length/2 later
  673.         rotateTransform.origin.x = 0.0;
  674.         rotateTransform.origin.y = 0.0;
  675.         rotateTransform.origin.z = - length * 0.5;
  676.  
  677.         orientationTransform = Q3MatrixTransform_New(&orientationMatrix);
  678.         if( orientationTransform )
  679.             Q3Group_AddObject(mainGroup, orientationTransform);
  680.         
  681.         // Now we get the custom elements for this model
  682.         Q3Shape_GetSet(mainGroup, &set);
  683.                             
  684.         if( set ) {
  685.             // let's get the scale
  686.             if( Q3Set_Contains( set, kElementTypeScale) == kQ3True ) {
  687.                 Q3Set_Get(set, kElementTypeScale, &scale);
  688.             }
  689.  
  690.             // let's get the up vector
  691.             if( Q3Set_Contains( set, kElementTypeUpVector) == kQ3True ) {
  692.                 TQ3Vector3D            cameraUpVector = {0.0, 1.0, 0.0},
  693.                                     upVector;
  694.                 TQ3TransformObject    tempTransform;
  695.                 
  696.                 Q3Set_Get(set, kElementTypeUpVector, &upVector);
  697.                 
  698.                 Q3Vector3D_Normalize(&upVector, &upVector);
  699.                 
  700.                 // Get the axis for the rotation
  701.                 Q3Vector3D_Cross(&cameraUpVector, &upVector, &rotateTransform.orientation);
  702.                 // Check to see that the vectors are not co-linear
  703.                 // WARNING - the code is not dealing with opposing vectors (ie one vector point in
  704.                 // +X direction and the other in -X.  The length will be zero, and so will the dot
  705.                 // product.  You will have to generate an arbitrary axis for the 180° rotation.
  706.                 if( Q3Vector3D_Length( &rotateTransform.orientation ) != 0.0) {
  707.                     Q3Vector3D_Normalize(&rotateTransform.orientation, &rotateTransform.orientation);
  708.                     // Get the angle, only valid if vectors are unit length
  709.                     rotateTransform.radians = acos(Q3Vector3D_Dot(&cameraUpVector, &upVector));
  710.                     tempTransform = Q3RotateAboutAxisTransform_New(&rotateTransform);
  711.     
  712.                     Q3MatrixTransform_Set(orientationTransform,
  713.                         Q3Transform_GetMatrix(tempTransform, &orientationMatrix));
  714.                         
  715.                     Q3Object_Dispose(tempTransform);
  716.                 }
  717.             }
  718.  
  719.             // let's get the forward vector
  720.             if( Q3Set_Contains( set, kElementTypeForwardDirection) == kQ3True ) {
  721.                 TQ3Vector3D                        cameraViewVector = {0.0, 0.0, 1.0},
  722.                                                 forwardDirection;
  723.                 TQ3TransformObject                tempTransform;
  724.                 TQ3Matrix4x4                    matrixUp, matrixForward;
  725.                 
  726.                 Q3Set_Get(set, kElementTypeForwardDirection, &forwardDirection);
  727.                 
  728.                 Q3Vector3D_Normalize(&forwardDirection, &forwardDirection);
  729.  
  730.                 // Get the axis for the rotation
  731.                 Q3Vector3D_Cross(&cameraViewVector, &forwardDirection, &rotateTransform.orientation);
  732.                 // Check to see that the vectors are not co-linear
  733.                 // WARNING - the code is not dealing with opposing vectors (ie one vector point in
  734.                 // +X direction and the other in -X.  The length will be zero, and so will the dot
  735.                 // product.  You will have to generate an arbitrary axis for the 180° rotation.
  736.                 if( Q3Vector3D_Length( &rotateTransform.orientation ) != 0.0) {
  737.                     Q3Vector3D_Normalize(&rotateTransform.orientation, &rotateTransform.orientation);
  738.                     // Get the angle, only valid if vectors are unit length
  739.                     rotateTransform.radians = acos(Q3Vector3D_Dot(&cameraViewVector, &forwardDirection));
  740.                     tempTransform = Q3RotateAboutAxisTransform_New(&rotateTransform);
  741.                     
  742.                     Q3Transform_GetMatrix(tempTransform, &matrixForward);
  743.     
  744.                     // concatenate with previous rotation, getting it from the matrix
  745.                     Q3Transform_GetMatrix(orientationTransform, &matrixUp);
  746.                     
  747.                     Q3MatrixTransform_Set(orientationTransform,
  748.                         Q3Matrix4x4_Multiply(&matrixUp, &matrixForward, &orientationMatrix));
  749.                         
  750.                     Q3Object_Dispose(tempTransform);
  751.                 }
  752.             }
  753.             
  754.             Q3Object_Dispose(set);
  755.         }
  756.         
  757.         // Now we'll deal with the scale
  758.  
  759.         largestDimension = width;
  760.         if( largestDimension < height)
  761.             largestDimension = height;
  762.         if( largestDimension < length)
  763.             largestDimension = length;
  764.  
  765.         largestDimension *= scale;
  766.         
  767.         if(    largestDimension > theDocument->units ) {
  768.                 short    itemHit ;
  769.                 
  770.                 itemHit = StopAlert( LargeObject_Alert, nil ) ;
  771.                 
  772.                 if( itemHit == 1 ){
  773.                     MenuHandle    hMenu;
  774.                     hMenu = GetMenu(Settings_Menu);
  775.  
  776.                     CheckItem(hMenu, Scale1M, false);
  777.                     CheckItem(hMenu, Scale10M, false);
  778.                     CheckItem(hMenu, Scale100M, false);
  779.                     CheckItem(hMenu, Scale1KM, false);
  780.                     
  781.                     if( largestDimension <= 10.0 ) {
  782.                         // the working units become 10 meters
  783.                         // scale the object down by 10
  784.                         if( largestDimension < 10.0)
  785.                             scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.1;
  786.                         Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
  787.                         theDocument->units = 10.0;
  788.                         CheckItem(hMenu, Scale10M, true);
  789.                     } else if( largestDimension <= 100.0 ) {
  790.                         // the working units become 100 meters
  791.                         // scale the object down by 100
  792.                         if( largestDimension < 100.0)
  793.                             scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.01;
  794.                         Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
  795.                         theDocument->units = 100.0;
  796.                         CheckItem(hMenu, Scale100M, true);
  797.                     } else if( largestDimension <= 1000.0 ) {
  798.                         // the working units become 1000 meters
  799.                         // scale the object down by 1000
  800.                         if( largestDimension < 1000.0)
  801.                             scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.001;
  802.                         Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
  803.                         theDocument->units = 1000.0;
  804.                         CheckItem(hMenu, Scale1KM, true);
  805.                     } else {
  806.                         itemHit = StopAlert( TooLargeObject_Alert, nil ) ;
  807.                     }
  808.                 }                
  809.         }
  810.          
  811.         // add a translate transform to center the object in the scene
  812.  
  813.         // move the object so that the front is at the view plane (Z direction) and centered in x and y
  814.         objectFront.x = - (viewBBox.min.x + width * 0.5);
  815.         objectFront.y = - (viewBBox.min.y + height * 0.5);
  816.         // We are looking into negative z, that's why we use maximum
  817.         objectFront.z = - viewBBox.max.z;
  818.         
  819.         centerTransform = Q3TranslateTransform_New(&objectFront);
  820.         Q3Group_AddObject(mainGroup, centerTransform);
  821.         Q3Object_Dispose(centerTransform);
  822.         
  823.         if( scaleTransform )
  824.             Q3Object_Dispose(scaleTransform);
  825.     }
  826.  
  827.     Q3ViewAngleAspectCamera_SetAspectRatio(
  828.         camera, (float) winWidth / (float) winHeight);
  829.     
  830.     Q3Object_Dispose(camera);
  831.     
  832.     return;
  833. }
  834.  
  835. // We use this function to do a quick check for attributes, to help us decide whether it's enough to look at 
  836. // the geometry's attribute set level, or if we need to go up to the shape's set
  837.  
  838. static TQ3Boolean CheckSetForAttributes(TQ3SetObject set) 
  839. {
  840.     if( Q3Set_Contains( set, kElementTypeName) == kQ3True )
  841.         return kQ3True;
  842.         
  843.     if( Q3Set_Contains( set, kElementTypeW3Anchor) == kQ3True )
  844.         return kQ3True;
  845.         
  846.     return kQ3False;
  847. }
  848.  
  849. // This function gets the attributes, if they exists, and display them
  850. static TQ3Boolean FindAndDisplayCustomAttributes(DocumentPtr theDocument, TQ3Object object)
  851. {
  852.     TQ3SetObject        set = NULL;
  853.     RGBColor            color = {0,0,0};
  854.  
  855.     RGBForeColor(&color);
  856.     TextMode(srcXor);
  857.     
  858.     if( Q3Object_IsType(object, kQ3ShapeTypeGeometry) == kQ3True) {
  859.         Q3Geometry_GetAttributeSet(object, &set);
  860.         if( set && CheckSetForAttributes(set) == kQ3False ) {
  861.             /* Let's check at the shape set level */
  862.             Q3Object_Dispose(set);
  863.             set = NULL;
  864.         }
  865.     }
  866.     
  867.     if(set == NULL && Q3Object_IsType(object, kQ3SharedTypeShape) == kQ3True){
  868.         Q3Shape_GetSet(object, &set);
  869.     }
  870.         
  871.     if( set ) {
  872.         if( Q3Set_Contains( set, kElementTypeName) == kQ3True ) {
  873.             Str255            pascalString;
  874.             TQ3StringObject    string;
  875.             
  876.             Q3Set_Get( set, kElementTypeName, &string);
  877.  
  878.             if( theDocument->fStringObject != string ) {
  879.                 /* Delete previously displayed text */
  880.                 if( theDocument->fStringObject ) {
  881.                     Str255            pascalString;
  882.  
  883.                     strcpy((char *) pascalString, theDocument->fName);
  884.                     MoveTo(10,10);
  885.                     DrawString(pascalString);
  886.                     
  887.                     Q3CString_EmptyData(&theDocument->fName);
  888.                     Q3Object_Dispose(theDocument->fStringObject);
  889.                 }
  890.                 
  891.                 theDocument->fStringObject = string;
  892.                 
  893.                 Q3CString_GetString(theDocument->fStringObject, &theDocument->fName);
  894.                 
  895.                 strcpy((char *) pascalString, theDocument->fName);
  896.                 MoveTo(10,10);
  897.                 DrawString(c2pstr((char *)pascalString));
  898.             }
  899.         }
  900.  
  901.         if( Q3Set_Contains( set, kElementTypeW3Anchor) == kQ3True ) {
  902.             Str255                pascalString;
  903.             W3AnchorData    urlData;
  904.             
  905.             Q3Set_Get( set, kElementTypeW3Anchor, &urlData);
  906.  
  907.             if( theDocument->fDescription != urlData.description) {
  908.                 if( theDocument->fDescription ) {
  909.                     /* Delete previously displayed url */
  910.                     MoveTo(10,30);
  911.                     DrawString(theDocument->fURL);
  912.     
  913.                     MoveTo(10,50);
  914.                     strcpy((char *) pascalString, theDocument->fDescString);
  915.                     DrawString(c2pstr((char *)pascalString));
  916.                     
  917.                     Q3CString_EmptyData(&theDocument->fDescString);
  918.                     
  919.                     Q3Object_Dispose(theDocument->fDescription);
  920.                 }
  921.     
  922.                 MoveTo(10,30);
  923.                 strcpy((char *) theDocument->fURL, urlData.url);
  924.                 c2pstr((char *)theDocument->fURL);            
  925.                 DrawString(theDocument->fURL);
  926.     
  927.                 MoveTo(10,50);
  928.                 Q3CString_GetString(urlData.description, &theDocument->fDescString);
  929.                 strcpy((char *) pascalString, theDocument->fDescString);
  930.                 DrawString(c2pstr((char *)pascalString));
  931.                 
  932.                 theDocument->fDescription = urlData.description;
  933.             }
  934.             
  935.             if( theDocument->launchNetscape == kQ3True ) {
  936.                 if( OpenURL(urlData.url) == false)
  937.                     StopAlert( Netscape_Alert, nil );
  938.             }
  939.         }
  940.         
  941.         Q3Object_Dispose(set);
  942.         return( kQ3True );
  943.     }             
  944.     return( kQ3False );
  945. }
  946.  
  947. void DisplayCustomAttributes(DocumentPtr theDocument)
  948. {
  949.     TQ3HitData    hitData;
  950.     
  951.     Q3Pick_GetHitData(theDocument->fPickObject, 0, &hitData);
  952.     
  953.     if( hitData.object ) {
  954.         FindAndDisplayCustomAttributes(theDocument, hitData.object);
  955.     }
  956.     Q3Pick_EmptyHitList(theDocument->fPickObject);
  957.     Q3Hit_EmptyData(&hitData);
  958. }
  959.  
  960.